package com.bym.mall.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bym.mall.entity.*;
import com.bym.mall.enums.OrderStatusEnum;
import com.bym.mall.enums.PaymentTypeEnum;
import com.bym.mall.enums.ProductStatusEnum;
import com.bym.mall.enums.ResponseEnum;
import com.bym.mall.mapper.OrderItemMapper;
import com.bym.mall.mapper.OrderMapper;
import com.bym.mall.mapper.ProductMapper;
import com.bym.mall.mapper.ShippingMapper;
import com.bym.mall.service.ICartService;
import com.bym.mall.service.IOrderItemService;
import com.bym.mall.service.IOrderService;
import com.bym.mall.utils.UserUtil;
import com.bym.mall.vo.OrderItemVo;
import com.bym.mall.vo.OrderVo;
import com.bym.mall.vo.ResponseVo;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author bym
 * @since 2022-06-22
 */
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {

    @Resource
    private ShippingMapper shippingMapper;

    @Resource
    private ProductMapper productMapper;

    @Resource
    private OrderMapper orderMapper;

    @Resource
    private OrderItemMapper orderItemMapper;

    @Autowired
    private ICartService cartService;

    @Autowired
    private IOrderItemService orderItemService;

    @Autowired
    private RedissonClient redissonClient;

    /**
     * 创建订单vo
     *
     * @param shippingId 地址id
     * @return {@link ResponseVo}<{@link OrderVo}>
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public ResponseVo<OrderVo> create(Integer shippingId) {
        //收获地址校验（总之是要查出来的）
        Integer uid = UserUtil.getUser().getId();
        QueryWrapper<Shipping> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", shippingId);
        queryWrapper.eq("user_id", uid);
        Shipping shipping = shippingMapper.selectOne(queryWrapper);
        if (ObjectUtil.isNull(shipping)) {
            ResponseVo.error(ResponseEnum.SHIPPING_NOT_EXIST);
        }

        //获取购物车，校验（是否有商品、库存）
        List<Cart> cartList = cartService.listForCart().stream()
                .filter(Cart::getSelected)
                .collect(Collectors.toList());
        if (CollectionUtil.isEmpty(cartList)) {
            ResponseVo.error(ResponseEnum.CART_SELECTED_IS_EMPTY);
        }

        //获取cartList里的productId
        List<Integer> idList = cartList.stream()
                .map(Cart::getProductId)
                .collect(Collectors.toList());
        List<Product> productList = productMapper.selectBatchIds(idList);
        Map<Integer, Product> map = productList.stream()
                .collect(Collectors.toMap(Product::getId, product -> product));

        ArrayList<OrderItem> orderItemList = new ArrayList<>();
        Long orderNo = generateOrderNo();
        for (Cart cart : cartList) {
            //根据productId查询数据库
            Product product = map.get(cart.getProductId());
            //是否有商品
            if (ObjectUtil.isNull(product)) {
                return ResponseVo.error(ResponseEnum.PRODUCT_NOT_EXIST);
            }

            //生成订单时的库存操作需要保证互斥性
            RLock lock = redissonClient.getLock("OrderService:create:lock:" + product.getId());
            try {
                //redisson加锁
                boolean lockRes = lock.tryLock(10, TimeUnit.SECONDS);
                if (lockRes) {
                    //商品上下架状态
                    if (!ProductStatusEnum.ON_SALE.getCode().equals(product.getStatus())) {
                        return ResponseVo.error(ResponseEnum.PRODUCT_OFF_SALE_OR_DELETE, "商品不是在售状态:" + product.getName());
                    }

                    //库存是否充足
                    if (product.getStock() < cart.getQuantity()) {
                        return ResponseVo.error(ResponseEnum.PROODUCT_STOCK_ERROR, product.getName() + " 库存不正确");
                    }

                    //生成订单项
                    OrderItem orderItem = buildOrderItem(uid, orderNo, cart.getQuantity(), product);
                    orderItemList.add(orderItem);

                    //减少商品的库存
                    int nowStock = product.getStock() - orderItem.getQuantity();
                    product.setStock(nowStock);
                    int rowForProduct = productMapper.updateById(product);
                    if (rowForProduct <= 0) {
                        return ResponseVo.error(ResponseEnum.ERROR);
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                //redisson解锁
                lock.unlock();
            }
        }

        //计算总价，只计算选中的商品
        //生成订单，入库：order和order_item，使用事务保证原子性
        Order order = buildOrder(uid, orderNo, shippingId, orderItemList);
        int insertForOrder = orderMapper.insert(order);
        if (insertForOrder <= 0) {
            return ResponseVo.error(ResponseEnum.ERROR);
        }
        boolean insertForOrderItems = orderItemService.saveBatch(orderItemList);
        if (!insertForOrderItems) {
            return ResponseVo.error(ResponseEnum.ERROR);
        }

        //todo:可以用lua脚本保证redis执行多命令时的原子性
        //更新购物车（选中的商品）
        for (Cart cart : cartList) {
            cartService.delete(cart.getProductId());
        }

        //构造orderVo
        OrderVo orderVo = buildOrderVo(order, orderItemList, shipping);

        return ResponseVo.success(orderVo);
    }

    @Override
    public ResponseVo list(Integer pageNum, Integer pageSize) {
        //todo：改成分页查询

        Integer uid = UserUtil.getUser().getId();

        //查询用户的所有订单
        QueryWrapper<Order> queryWrapper1 = new QueryWrapper<>();
        queryWrapper1.eq("user_id", uid);
        List<Order> orderList = orderMapper.selectList(queryWrapper1);

        //查询用户所有的订单项
        QueryWrapper<OrderItem> queryWrapper2 = new QueryWrapper<>();
        queryWrapper2.eq("user_id", uid);
        List<OrderItem> orderItemList = orderItemMapper.selectList(queryWrapper2);
        Map<Long, List<OrderItem>> orderItemMap = new HashMap<>(8);
        for (OrderItem orderItem : orderItemList) {
            Long orderNo = orderItem.getOrderNo();
            List<OrderItem> orderItems = orderItemMap.getOrDefault(orderNo, new ArrayList<>());
            orderItems.add(orderItem);
        }

        //查询用户所有的收货地址
        QueryWrapper<Shipping> queryWrapper3 = new QueryWrapper<>();
        queryWrapper3.eq("user_id", uid);
        List<Shipping> shippingList = shippingMapper.selectList(queryWrapper3);
        Map<Integer, Shipping> shippingMap = new HashMap<>(8);
        for (Shipping shipping : shippingList) {
            shippingMap.put(shipping.getId(), shipping);
        }

        //将订单、订单项和收获地址进行对应
        ArrayList<OrderVo> orderVoList = new ArrayList<>();
        for (Order order : orderList) {
            List<OrderItem> orderItems = orderItemMap.get(order.getOrderNo());
            Shipping shipping = shippingMap.get(order.getShippingId());
            OrderVo orderVo = buildOrderVo(order, orderItems, shipping);
            orderVoList.add(orderVo);
        }

        return ResponseVo.success(orderVoList);
    }

    @Override
    public ResponseVo detail(Long orderNo) {
        //查询订单
        Integer uid = UserUtil.getUser().getId();
        QueryWrapper<Order> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("order_no", orderNo);
        queryWrapper.eq("user_id", uid);
        Order order = orderMapper.selectOne(queryWrapper);
        if (ObjectUtil.isNull(order)) {
            return ResponseVo.error(ResponseEnum.ERROR);
        }

        //查询订单项
        QueryWrapper<OrderItem> queryWrapper1 = new QueryWrapper<>();
        queryWrapper1.eq("order_no", orderNo);
        queryWrapper1.eq("user_id", uid);
        List<OrderItem> orderItemList = orderItemMapper.selectList(queryWrapper1);
        if (ObjectUtil.isNull(orderItemList)) {
            return ResponseVo.error(ResponseEnum.ERROR);
        }

        //查询收货地址
        Shipping shipping = shippingMapper.selectById(order.getShippingId());
        if (ObjectUtil.isNull(shipping)) {
            return ResponseVo.error(ResponseEnum.ERROR);
        }

        //构建orderVo
        OrderVo orderVo = buildOrderVo(order, orderItemList, shipping);

        return ResponseVo.success(orderVo);
    }

    @Override
    public ResponseVo cancel(Long orderNo) {
        //查询订单
        Integer uid = UserUtil.getUser().getId();
        QueryWrapper<Order> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("order_no", orderNo);
        queryWrapper.eq("user_id", uid);
        Order order = orderMapper.selectOne(queryWrapper);
        if (ObjectUtil.isNull(order)) {
            return ResponseVo.error(ResponseEnum.ERROR);
        }

        //判断订单状态
        if (!order.getStatus().equals(OrderStatusEnum.NO_PAY.getCode())) {
            return ResponseVo.error(ResponseEnum.ORDER_STATUS_ERROR);
        }

        //更新订单状态
        order.setStatus(OrderStatusEnum.CANCELED.getCode());
        order.setCloseTime(LocalDateTime.now());
        int row = orderMapper.updateById(order);
        if (row <= 0) {
            return ResponseVo.error(ResponseEnum.ERROR);
        }

        return ResponseVo.success();
    }

    @Override
    public void paid(Long orderNo) {
        //查询订单
        Integer uid = UserUtil.getUser().getId();
        QueryWrapper<Order> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("order_no", orderNo);
        queryWrapper.eq("user_id", uid);
        Order order = orderMapper.selectOne(queryWrapper);
        if (ObjectUtil.isNull(order)) {
            throw new RuntimeException(ResponseEnum.ORDER_NOT_EXIST.getDesc() + ":订单不存在，订单id:" + orderNo);
        }

        //判断订单状态
        if (!order.getStatus().equals(OrderStatusEnum.NO_PAY.getCode())) {
            throw new RuntimeException(ResponseEnum.ORDER_STATUS_ERROR.getDesc() + ":订单状态异常，订单id:" + orderNo);
        }

        //更新订单状态
        order.setStatus(OrderStatusEnum.PAID.getCode());
        order.setPaymentTime(LocalDateTime.now());
        int row = orderMapper.updateById(order);
        if (row <= 0) {
            throw new RuntimeException("将订单更新为已支付状态失败，订单id:" + orderNo);
        }
    }

    /**
     * 企业级：分布式唯一id
     *
     * @return {@link Long}
     */
    private Long generateOrderNo() {
        return System.currentTimeMillis() + new Random().nextInt(10000);
    }

    /**
     * 建立订单项
     *
     * @return {@link OrderItem}
     */
    private OrderItem buildOrderItem(Integer uid,
                                     Long orderNo,
                                     Integer quantity,
                                     Product product) {
        OrderItem orderItem = OrderItem.builder()
                .userId(uid)
                .orderNo(orderNo)
                .productId(product.getId())
                .productName(product.getName())
                .productImage(product.getMainImage())
                .currentUnitPrice(product.getPrice())
                .quantity(quantity)
                .totalPrice(product.getPrice().multiply(BigDecimal.valueOf(quantity)))
                .build();

        return orderItem;
    }

    private Order buildOrder(Integer uid,
                             Long orderNo,
                             Integer shippingId,
                             List<OrderItem> orderItemList) {
        BigDecimal payment = new BigDecimal(0);
        for (OrderItem orderItem : orderItemList) {
            payment = payment.add(orderItem.getTotalPrice());
        }

        Order order = Order.builder()
                .userId(uid)
                .orderNo(orderNo)
                .shippingId(shippingId)
                .payment(payment)
                .paymentType(PaymentTypeEnum.PAY_ONLINE.getCode())
                .postage(0)
                .status(OrderStatusEnum.NO_PAY.getCode())
                .build();

        return order;
    }

    /**
     * 生成orderVo
     *
     * @return {@link OrderVo}
     */
    private OrderVo buildOrderVo(Order order, List<OrderItem> orderItemList, Shipping shipping) {
        OrderVo orderVo = new OrderVo();
        BeanUtils.copyProperties(order, orderVo);

        List<OrderItemVo> orderItemVoList = orderItemList.stream().map(e -> {
            OrderItemVo orderItemVo = new OrderItemVo();
            BeanUtils.copyProperties(e, orderItemVo);
            return orderItemVo;
        }).collect(Collectors.toList());
        orderVo.setOrderItemVoList(orderItemVoList);

        if (shipping != null) {
            orderVo.setShippingId(shipping.getId());
            orderVo.setShippingVo(shipping);
        }

        return orderVo;
    }
}
